#include <windows.h>
#include <stdio.h>
#include "api.h"
#include "image.h"
#include "data.h"
#include "colorswnd.h"
#include "buttonswnd.h"
#include "palettewnd.h"
#include "resource.h"
#include "filedlg.h"

#define PALETTE_FILE_FILTER "Palette (*.pal)\0*.pal\0\0"
#define MAX_COLORPAGE NRO_COLORSPAGE-1

extern PDATA data;

#define colorsClassName "COLORSWND"
// Message map
#define BM_PALETTE WM_USER+1	
	#define BUTTON_NEW		0
	#define BUTTON_OPEN		1
	#define BUTTON_SAVE		2
	#define BUTTON_SAVE_AS	3
	#define BUTTON_PREVPAGE	4
	#define BUTTON_NEXTPAGE	5
#define BM_COLORS WM_USER+2
	#define BUTTON_COPY		0
	#define BUTTON_PASTE	1
	#define BUTTON_LIGHT	2
	#define BUTTON_DARK		3
	#define BUTTON_GRAY		4
	#define BUTTON_AVERAGE	5
	#define BUTTON_UP		6
	#define BUTTON_DOWN		7
	#define BUTTON_INSERT	8
	#define BUTTON_DELETE	9

COLORREF colorsWndToColor(COLORREF source, COLORREF target){
	int sr = GetRValue(source);
	int sg = GetGValue(source);
	int sb = GetBValue(source);
	int tr = GetRValue(target);
	int tg = GetGValue(target);
	int tb = GetBValue(target);
	return RGB((sr*2+tr)/3, (sg*2+tg)/3, (sb*2+tb)/3);
}

COLORREF colorsWndToAverage(COLORREF source, COLORREF target){
	int sr = GetRValue(source);
	int sg = GetGValue(source);
	int sb = GetBValue(source);
	int tr = GetRValue(target);
	int tg = GetGValue(target);
	int tb = GetBValue(target);
	return RGB((sr+tr)/2, (sg+tg)/2, (sb+tb)/2);
}

RECT rPainter	= {5,5,25,25};
RECT rBorder	= {10,10,30,30};
RECT rInk		= {45,5,65,25};
RECT rPaper		= {50,10,70,30};

void colorsWndAverageEntry(PCOLORSWND data){
	COLORREF color;
	if (data->iColor==0){
		color = data->curPage->color[1].color;
	}else
	if (data->iColor==MAX_COLORPAGE){
		color = data->curPage->color[MAX_COLORPAGE-1].color;
	}
	else{
		color = colorsWndToAverage(data->curPage->color[data->iColor-1].color, data->curPage->color[data->iColor+1].color);
	}
	data->curPage->color[data->iColor].color = color;	
}

void colorsWndIniData(PCOLORSWND data){
	int i, j;
	for(i=0;i<NRO_PAGES;i++){
		PCOLORPAGE page = data->page+i;
		strcpy(page->name,"Color");
		for(j=0;j<NRO_COLORSPAGE;j++){
			PCOLORENTRY color = page->color+j;
			color->color = 0xFFFFFF;
			color->flg = 0;
		}
	}
	data->brushBack = CreateSolidBrush(0xA0A0A0);
	data->brushSelect = CreateSolidBrush(0x808080);
	data->curPage = data->page;
}

void colorsWndOnCreate(HWND hwnd, PCOLORSWND data){
	buttonsWndCreate(hwnd, 80,0,IDB_PALETTE, 20, 20, BM_PALETTE);
	buttonsWndCreate(hwnd, 0, 50,IDB_COLORS, 20, 20, BM_COLORS);
	data->hwndPalette = paletteWndCreate(hwnd, 0, 70, 198, 400, data);
}

void colorsWndDestroy(HWND hwnd, PCOLORSWND data){
	DeleteObject(data->brushBack);
	DeleteObject(data->brushSelect);
	FREE_DATA();
}

COLORREF colorsWndToGray(COLORREF source){
	int sr = GetRValue(source);
	int sg = GetGValue(source);
	int sb = GetBValue(source);
	int g = (sr+sg+sb)/3;
	return colorsWndToColor(source, RGB(g, g, g));
}

void colorsWndSwap(PCOLORPAGE page, int a, int b){
	COLORENTRY tmp = page->color[a];
	page->color[a] = page->color[b];
	page->color[b] = tmp;
}

BOOL colorsWndColorUp(PCOLORSWND data){
	if (data->iColor<=0){
		return FALSE;
	}
	colorsWndSwap(data->curPage, data->iColor, data->iColor-1);
	data->iColor--;
	return TRUE;
}

BOOL colorsWndColorDown(PCOLORSWND data){
	if (data->iColor>=MAX_COLORPAGE){
		return FALSE;
	}
	colorsWndSwap(data->curPage, data->iColor, data->iColor+1);
	data->iColor++;
	return TRUE;
}

void colorsWndDeleteColor(PCOLORSWND data){
	int i;
	for(i=data->iColor;i<MAX_COLORPAGE;i++){
		data->curPage->color[i] = data->curPage->color[i+1];
	}
}

void colorsWndInsertColor(PCOLORSWND data){
	int i;
	for(i=MAX_COLORPAGE;i>data->iColor;i--){
		data->curPage->color[i] = data->curPage->color[i-1];
	}
}

void colorsWndSaveContent(char *fileName, PCOLORSWND data){
	FILE *fpalette = fopen(data->fileName, "wb+");
	fwrite(data->page, sizeof(COLORPAGE), NRO_PAGES, fpalette);
	fclose(fpalette);
}

void colorsWndLoadContent(char *fileName, PCOLORSWND data){
	FILE *fpalette = fopen(data->fileName, "rb+");
	fread(data->page, sizeof(COLORPAGE), NRO_PAGES, fpalette);
	fclose(fpalette);
}

void colorsWndSavePalette(HWND hwnd, PCOLORSWND data, BOOL askByName){
	char fileName[300];
	FILECALLER caller;
	if (!data->fileName[0] || askByName){
		caller.filter = PALETTE_FILE_FILTER;
		caller.hwndOwner = hwnd;
		caller.name = NULL;
		caller.directory = ::data->config->paletteDir;
		if (!fileSaveFile(&caller, fileName)){
			return;
		}
		strcpy(data->fileName, fileName);
	}

	colorsWndSaveContent(data->fileName, data);
	data->isChanged = FALSE;
}

void colorsWndSaveChanges(HWND hwnd, PCOLORSWND data){
	if (data->isChanged){
		if (MessageBox(hwnd, "Desea guardar los cambios a la paleta de colores?", MESSAGE_TITLE, MB_YESNO)==IDYES){
			colorsWndSavePalette(hwnd, data, FALSE);
		}else{
			data->isChanged = FALSE;	// Reset the change flag
		}
	}
}

void colorsWndNewPalette(HWND hwnd, PCOLORSWND data){
	colorsWndSaveChanges(hwnd, data);
	if (data->isChanged){				
		return;
	}
	data->fileName[0] = 0;
	colorsWndIniData(data);
	InvalidateRect(data->hwndPalette, NULL, FALSE);
}

void colorsWndOpenPalette(HWND hwnd, PCOLORSWND data){
	char fileName[300];
	FILECALLER caller;
	colorsWndSaveChanges(hwnd, data);
	if (data->isChanged){
		return;
	}
	caller.filter = PALETTE_FILE_FILTER;
	caller.hwndOwner = hwnd;
	caller.name = NULL;
	caller.directory = ::data->config->paletteDir;
	if (fileLoadFile(&caller, fileName)){
		strcpy(data->fileName, fileName);
		colorsWndLoadContent(data->fileName, data);
		data->iColor = 0;
		data->iPage = 0;
		InvalidateRect(data->hwndPalette, NULL, FALSE);
	}
}

void colorsWndPalette(HWND hwnd, int index, PCOLORSWND data){
	BOOL redraw = FALSE;
	switch(index){
	case BUTTON_NEW:
		colorsWndNewPalette(hwnd, data);
		break;
	case BUTTON_OPEN:
		colorsWndOpenPalette(hwnd, data);
		break;
	case BUTTON_SAVE:
		colorsWndSavePalette(hwnd, data, FALSE);
		break;
	case BUTTON_SAVE_AS:
		colorsWndSavePalette(hwnd, data, TRUE);
		break;
	case BUTTON_PREVPAGE:
		if (data->iPage){
			data->iPage--;
			data->curPage = data->page + data->iPage;
			redraw = TRUE;
		}
		break;
	case BUTTON_NEXTPAGE:
		if (data->iPage<NRO_PAGES-1){
			data->iPage++;
			data->curPage = data->page + data->iPage;
			redraw = TRUE;
		}
		break;
	}
	if (redraw){
		InvalidateRect(data->hwndPalette, NULL, FALSE);
	}
}

void colorsWndColors(HWND hwnd, int index, PCOLORSWND data){
	PCOLORENTRY color = &data->curPage->color[data->iColor];
	BOOL redraw = FALSE;
	switch(index){
	case BUTTON_COPY:
		data->bufferColor = color->color;
		break;
	case BUTTON_PASTE:
		color->color = data->bufferColor;
		redraw = TRUE;
		break;
	case BUTTON_LIGHT:
		color->color = colorsWndToColor(color->color, 0xFFFFFF);
		redraw = TRUE;
		break;
	case BUTTON_DARK:
		color->color = colorsWndToColor(color->color, 0x000000);
		redraw = TRUE;
		break;
	case BUTTON_GRAY:
		color->color = colorsWndToGray(color->color);
		redraw = TRUE;
		break;
	case BUTTON_AVERAGE:
		colorsWndAverageEntry(data);
		redraw = TRUE;
		break;
	case BUTTON_UP:
		redraw = colorsWndColorUp(data);
		break;
	case BUTTON_DOWN:
		redraw = colorsWndColorDown(data);
		break;
	case BUTTON_INSERT:
		colorsWndInsertColor(data);
		redraw = TRUE;
		break;
	case BUTTON_DELETE:
		colorsWndDeleteColor(data);
		redraw = TRUE;
		break;
	}
	if (redraw){
		data->isChanged = TRUE;
		InvalidateRect(data->hwndPalette, NULL, FALSE);
	}
}

void colorsWndFillRect(HDC pdc, RECT *r, COLORREF color){
	HBRUSH brush = CreateSolidBrush(color);
	FillRect(pdc, r, brush);
	DeleteObject(brush);
}

void colorsWndOnPaint(HWND hwnd, HDC pdc, PCOLORSWND data){
	colorsWndFillRect(pdc, &rBorder, ::data->colorBorder);
	colorsWndFillRect(pdc, &rPainter, ::data->colorPainter);
	colorsWndFillRect(pdc, &rPaper, ::data->colorPaper);
	colorsWndFillRect(pdc, &rInk, ::data->colorInk);
}

LRESULT CALLBACK colorsWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){
	GET_DATA(PCOLORSWND);
	switch(message){
	case BM_PALETTE:
		colorsWndPalette(hwnd, wParam, data);
		break;
	case BM_COLORS:
		colorsWndColors(hwnd, wParam, data);
	case WM_CREATE:
		colorsWndOnCreate(hwnd, data);
		break;
	case WM_DESTROY:
		colorsWndDestroy(hwnd, data);
		break;
	case WM_PAINT:
		ON_PAINT(colorsWndOnPaint);
	}
	return DefWindowProc(hwnd, message, wParam, lParam);
}

HWND colorsWndCreate(HWND hwndParent, int x, int y, int dx, int dy)
{
    HWND hwnd;
	NEW_DATA(COLORSWND);	
	colorsWndIniData(data);

    hwnd = CreateWindowEx(0,
           colorsClassName,
           "",
           WS_VISIBLE | WS_CLIPCHILDREN | WS_CHILD,
           x,
           y,
           dx,
           dy,		   
           hwndParent,
           NULL,
           (HINSTANCE)GetWindowLong(hwndParent, GWL_HINSTANCE),
		   data
           );
    ShowWindow (hwnd, SW_SHOW);
    return hwnd;
}

int colorsWndRegister(HINSTANCE instancia)
{
    WNDCLASSEX wincl;

    wincl.hInstance = instancia;
    wincl.lpszClassName = colorsClassName;
    wincl.lpfnWndProc = colorsWndProc;
    wincl.style = CS_DBLCLKS;
    wincl.cbSize = sizeof (WNDCLASSEX);

    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL;
    wincl.cbClsExtra = 0;
    wincl.cbWndExtra = 0;
    wincl.hbrBackground = CreateSolidBrush(0xC0C0C0);

    if (!RegisterClassEx (&wincl))
        return 0;
	paletteWndRegister(instancia);
    return TRUE;
}